<!doctype html>
<html lang="en">

	<head>
		<meta charset="utf-8">

		<title>'Embedding' a Meta State Machine</title>

		<meta name="description" content="'Embedding' a Meta State Machine">
		<meta name="author" content="Kris Jusiak">

		<meta name="apple-mobile-web-app-capable" content="yes">
		<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">

		<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, minimal-ui">

		<link rel="stylesheet" href="css/reveal.css">
    <link rel="stylesheet" href="css/theme/league.css" id="theme">

		<!-- Code syntax highlighting -->
    <link rel="stylesheet" href="lib/css/zenburn.css">

		<!-- Printing and PDF exports -->
		<script>
			var link = document.createElement( 'link' );
			link.rel = 'stylesheet';
			link.type = 'text/css';
			link.href = window.location.search.match( /print-pdf/gi ) ? 'css/print/pdf.css' : 'css/print/paper.css';
			document.getElementsByTagName( 'head' )[0].appendChild( link );
		</script>

		<!--[if lt IE 9]>
		<script src="lib/js/html5shiv.js"></script>
		<![endif]-->
	</head>

	<body>

		<div class="reveal">
			<div class="slides">

          <section data-markdown=""
                   data-separator="^====+$"
                   data-separator-vertical="^----+$"
                   data-notes="^Note:">
					<script type="text/template">

emBO++ 2017

#'Embedding' a Meta State Machine

Kris Jusiak, Quantlab

---

[kris@jusiak.net](mailto:kris@jusiak.net) | [@krisjusiak](https://twitter.com/krisjusiak) | [linkedin.com/in/kris-jusiak](https://www.linkedin.com/in/kris-jusiak)

==============================================================================

##Agenda

* Connection example
  * Implementation
    * Naive
    * Enum/Switch
    * Variant (C++17)
    * State Machine Language (SML)
* [Boost].SML
  * Overview
  * SML vs Boost.MSM vs Boost.Statechart
  * Design in a nutshell
* Summary

==============================================================================

##Story: Connection (BDD style)

<table>
<tr>
<td>
<pre>
Scenario 1: Establishing connection
  Given the connection is disconnected
  When the user requests to connect
  Then the establish request should be sent
  And the system should wait for estaliblished event
</pre>
</td>
<td>
<pre>
Scenario 2: Keeping connection
  Given the connection is connected
  When the valid ping is recieved
  Then the timeout should be reset
</pre>
</td>
</tr>

<tr>
<td>
<pre>
Scenario 3: Connection timeout
  Given the connection is connected
  When the timeout is recieved
  Then the establish should be called
  And connecting process start again
</pre>
</td>
<td>
<pre>
Scenario 4: Disconnecting
  Given the connection is connected
  When the disconnect event is recieved
  Then the close should be called
  And user should get disconnected
</pre>
</td>
</tr>
</table>
<!-- .element: style="margin-left:-13%; width:125%" -->

----

## Connection: State Diagram (UML 2.5)

![Connection](images/connection.png)

| |
|-|
|Transition - Unified Modeling Language (UML)|
|<center>![Transition](images/transition.png)</center>|

----

##Connection - Helpers/Details

```cpp
void resetTimeout() { std::puts("resetTimeout"); }
void establish() { std::puts("establish"); }
void close() { std::puts("close"); }
bool is_valid(const Ping&) { return true; }
```

----

##Connection (V1) - Naive Implementation

```cpp
class ConnectionV1 {
    bool disconnected = true, connected = false, connecting = false;
```

```cpp
public:
    void connect() {
        if (disconnected || connected) {
            establish();
            disconnected = false;
            connecting = true;
        }
    }
```

```cpp
    void disconnect() {
        if (connecting || connected) {
            close();
            connected = false;
            disconnected = true;
        }
    }
```

----

##Connection (V1) - Naive Implementation

```cpp
    void established() {
        connecting = false;
        connected = true;
    }
```

```cpp
    void ping(const Ping& event) {
        if (connected && is_valid(event)) {
            resetTimeout();
        }
    }
```

```cpp
    void timeout() { connect(); }
};
```

----

##Connection (V1) - Benchmark

|                      | GCC-7          | Clang-3.9      |
|----------------------|----------------|----------------|
| Compilation time     | 0.101s         | 0.122s         |
| sizeof(ConnectionV1) | 3b             | 3b             |
| Executable size      | 6.2K           | 6.2K           |

---

<table>
<tr>
<td>Connect: <br/>ASM x86-64</td>
<td>
<pre>
main:
  sub     rsp, 8
  mov     edi, OFFSET FLAT:.LC1
  call    puts
  xor     eax, eax
  add     rsp, 8
  ret
</pre>
</td>
</tr>
</table>
https://godbolt.org/g/0qpgvv

----

##Connection (V1) - Summary

* (+) Quick compilation times
* (+) Good performance
* (-) Uses more size than required
* (-) Hard to follow
* (-) Hard to extend and maintain
* (-) Hard to test

----

##Connection (V2) - Enum/Switch Implementation

```cpp
class ConnectionV2 {
    enum class State : unsigned char { DISCONNECTED, CONNECTING, CONNECTED } state;
```
<!-- .element: style="margin-left:-5%; width:105%" -->

```cpp
public:
    void connect() {
        switch(state) {
            default: break;
            case State::DISCONNECTED:
            case State::CONNECTED: establish(); state = State::CONNECTING; break;
        }
    }
```
<!-- .element: style="margin-left:-5%; width:105%" -->

```cpp
    void disconnect() {
        switch(state) {
            default: break;
            case State::CONNECTING:
            case State::CONNECTED: close(); state = State::DISCONNECTED; break;
        }
    }
```
<!-- .element: style="margin-left:-5%; width:105%" -->

----

##Connection (V2) - Enum/Switch Implementation

```cpp
    void established() {
        state = State::CONNECTED;
    }
```

```cpp
    void ping(const Ping& event) {
        if (state == State::CONNECTED && is_valid(event)) {
            resetTimeout();
        }
    }
```

```cpp
    void timeout() { connect(); }
};
```

----

##Connection (V2) - Benchmark

|                      | GCC-7          | Clang-3.9      |
|----------------------|----------------|----------------|
| Compilation time     | 0.112s         | 0.118s         |
| sizeof(ConnectionV2) | 1b             | 1b             |
| Executable size      | 6.2K           | 6.2K           |

---

<table>
<tr>
<td>Connect: <br/>ASM x86-64</td>
<td>
<pre>
main:
  sub     rsp, 8
  mov     edi, OFFSET FLAT:.LC1
  call    puts
  xor     eax, eax
  add     rsp, 8
  ret
</pre>
</td>
</tr>
</table>
https://godbolt.org/g/3Qphjb

----

##Connection (V2) - Summary

* (+) Quick compilation times
* (+) Good performance
* (+) Minimal size used
* (-) Hard to follow
* (-) Hard to extend and maintain
* (-) Hard to test

----

##Connection (V3) - C++17 Variant Implementation

```cpp
class ConnectionV3 {
    struct Disconnected { };
    struct Connecting { };
    struct Connected { };

    std::variant<Disconnected, Connecting, Connected> state;
```

```cpp
public:
    void connect() {
        std::visit(overload{
            [&](Disconnected) { establish(); state = Connecting{}; },
            [&](Connected) { establish(); state = Connecting{}; },
            [](auto) { }
        }, state);
    }
```

----

##Connection (V3) - C++17 Variant Implementation

```cpp
    void disconnect() {
        std::visit(overload{
            [&](Connecting) { close(); state = Disconnected{}; },
            [&](Connected) { close(); state = Disconnected{}; },
            [](auto) { }
        }, state);
    }
```

```cpp
    void established() {
        state = Connected{};
    }
```

```cpp
    void ping(const Ping& event) {
        if (std::get_if<Connected>(&state) && is_valid(event)) {
            resetTimeout();
        }
    }
```

```cpp
    void timeout() { connect(); }
};
```

----

##Connection (V3) - Benchmark

|                      | GCC-7          | Clang-3.9      |
|----------------------|----------------|----------------|
| Compilation time     | 0.272s         | 0.269s         |
| sizeof(ConnectionV3) | 2b             | 8b             |
| Executable size      | 6.2K           | 6.2K           |

---

<table>
<tr>
<td>Connect: <br/>ASM x86-64</td>
<td>
<pre>
main:
        sub     rsp, 56
        xor     eax, eax
        lea     rsi, [rsp+16]
        lea     rdi, [rsp+32]
        mov     WORD PTR [rsp+16], ax
        mov     QWORD PTR [rsp+8], rsi
        movq    xmm0, QWORD PTR [rsp+8]
        ...
</pre>
</td>
</tr>
</table>
https://godbolt.org/g/yf1BjN

----

##Connection (V3) - Summary

* (+) Data connected with the state naturally
* (+) Quick compilation times
* (-) Poor performance
* (-) Uses more size than required (different on GCC and Clang)
* (-) Hard to follow
* (-) Hard to extend and maintain
* (-) Hard to test

----

## Let's go back to the Connection State Diagram (UML 2.5)

![Connection](images/connection.png)

----

## Connection: Transition Table Representation (UML 2.5)

![](images/transition_table.png)

----

## Connection: Transition Table Text Representation (UML 2.5)

```cpp
* -> Disconnected : connect / establish              -> Connecting
```

```cpp
     Connecting   : established                      -> Connected
```

```cpp
     Connected    : ping [ is_valid ] / resetTimeout
```

```cpp
     Connected    : timeout                          -> Connecting
```

```cpp
     Connected    : disconnect                       -> Disconnected
```

----

##Let's introduce [Boost].SML
##Connection (V4)

```cpp
sml::sm connectionV4 = [] { // C++17 template arg. deduction for class templates
  return transition_table{
    * "Disconnected"_s + connect / establish                = "Connecting"_s,
      "Connecting"_s   + established                        = "Connected"_s,
      "Connected"_s    + ping [ is_valid ] / resetTimeout,
      "Connected"_s    + timeout / establish                = "Connecting"_s,
      "Connected"_s    + disconnect / close                 = "Disconnected"_s
  };
};
```
<!-- .element: style="width:105%" -->

----

##Connection (V4) - [Boost].SML

###Actions
```cpp
const auto establish = []{ std::puts("establish!"); };
const auto disconnect = []{ std::puts("disconnect!"); };
```

###Guards
```cpp
const auto is_valid = [](auto event){ return true; };
```

----

##Connection (V4) - Benchmark

|                      | GCC-7          | Clang-3.9      |
|----------------------|----------------|----------------|
| Compilation time     | 0.151s         | 0.169s         |
| sizeof(ConnectionV4) | 1b             | 1b             |
| Executable size      | 6.2K           | 6.2K           |

---

<table>
<tr>
<td>Connect: <br/>ASM x86-64</td>
<td>
<pre>
main:
        subq    $8, %rsp
        movl    $.LC0, %edi
        call    puts
        xorl    %eax, %eax
        addq    $8, %rsp
        ret
</pre>
</td>
</tr>
</table>
https://godbolt.org/g/6z6UF4

----

##Connection (V4) - Summary

* (+) Quick compilation times
* (+) Good performance
* (+) Minimal size used
* (+) Easy to follow
* (+) Easty to extend and maintain
* (+) Easy to test
* (-) Library has to be used (C++14)

----

##Connection - Benchmark

|                  | Naive  | Enum/Switch | Variant | [Boost].SML |
|------------------|------- |-------------|---------|-------------|
| Compilation time | 0.101s |      0.112s |  0.269s |      0.151s |
| sizeof(sm)       |     3b |          1b |   2b/8b |          1b |
| Executable size  |   6.2K |        6.2K |    6.2K |        6.2K |
| Performance (connect)   | inlined |      inlined |    not-inlined    |      inlined|

----

##More realist [benchmark](https://github.com/boost-ext/sml/tree/master/benchmark/complex)

![Benchmark](images/benchmark.png)

| Events | States | Transitions | Process Events |
| ------ | ------ | ----------- | -------------- |
| 50 | 50 | 50 | 1'000'000 |

----

##More realist [benchmark](https://github.com/boost-ext/sml/tree/master/benchmark/complex)

####Main
```cpp
int main() {
  for (auto i = 0; i < 1'000'000; ++i) {
    if (rand() % 2) sm.process_event(e1());
    if (rand() % 2) sm.process_event(e2());
    if (rand() % 2) sm.process_event(e3());
    ...
    if (rand() % 2) sm.process_event(e100());
  }
}
```

####CXXFLAGS
```
$CXX -std=c++1z -O3 -flto -fno-exceptions -DNDEBUG benchmark.cpp
```

----

##More realist [benchmark](https://github.com/boost-ext/sml/tree/master/benchmark/complex) - Results

|                  | Enum/Switch | Variant | [Boost].SML |
|------------------|-------------|---------|-------------|
| Compilation time |   0.132s | 15.321s | 0.582s |
| Execution time   |    679ms | 827ms   |  622ms |
| Memory usage     |       1b | 2b/8b   |     1b |
| Executable size  |      15K | 187K    |    34K |
| | | | |
| Line of Code (LOC) | ~300 (no macros) | ~300 | ~50 |

> Median / 100 runs

==============================================================================

##[Boost].SML

> UML-2.5 State Machine Language

https://github.com/boost-ext/sml

-----

##[[Boost].SML](http://boost-ext.github.io/sml)

* One header - 2k LOC - (boost/sml.hpp) / generated
* Neither Boost nor STL is required
* Quick compilation-times (-Wall -Wextra -Werror -pedantic -pedantic-errors)
* Blazing fast run-time (Generated at compile-time)
* No 'virtual's (-fno-rtti)
* Optional support for 'exception's (-fno-exceptions)
* Supported compilers (C++14)
  * [Clang-3.4+](https://travis-ci.org/boost-ext/sml), [XCode-6.1+](https://travis-ci.org/boost-ext/sml), [GCC-5.2+](https://travis-ci.org/boost-ext/di), [MSVC-2015+](https://ci.appveyor.com/project/krzysztof-jusiak/sml)

----

##[[Boost].SML](http://boost-ext.github.io/sml) - API (Simplified)

```cpp
/**
 * Makes transition table from DSL
 * @tparam Ts... transitions (transitional)
 */
template <class... Ts> requires transitional<Ts>()...
struct transition_table;
```

```cpp
/**
 * Helper function to make transition_table (C++14)
 * @tparam Ts... transitions (transitional)
 * @return transition_table
 */
template <class... Ts> requires transitional<Ts>()...
constexpr auto make_transition_table(Ts&&...) noexcept;
```

----

##[[Boost].SML](http://boost-ext.github.io/sml) - API (Simplified)

```cpp
/**
 * State Machine
 *
 * @tparam T Callable object returning transition_table
 * @tparam TPolicies policies to be applied such as
 *                   thread safe, exception safe, etc.
 */
template<class T,
         class... TPolicies> requires callable<transition_table, T>()
class sm;
```

```cpp
/**
 * Process event
 * Complexity - O(1)
 * @param TEvent event to be processed
 */
template<class TEvent>
constexpr void process_event(const TEvent&);
```

----

###[[Boost].SML](http://boost-ext.github.io/sml) - Features

* UML (2.5)
  * Transition
    * Anonymous, Internal, Self, ~~Local~~ transition
    * Guard, Action
  * Unexpected, Deffered, Any event
  * State
    * Entry/Exit Actions, Initial, Terminate state
    * **Composite/Sub state**
      * Explicit entry/exit, ~~Fork~~
      * Shallow History, Deep History
  * **Orthogonal regions**
* Non-UML
  * Logging, State visitor, Diagram gen., Run-time dispatch

----

###[[Boost].SML](http://boost-ext.github.io/sml) - Features

We DON'T pay for features we are NOT using!

> For example, if the state machine doesn't use orthogonal regions, the
code responsible for handling them won't be even generated

----

###[[Boost].SML](http://boost-ext.github.io/sml) - More realistic example (System)

![system](images/system.png)

> Enum/Switch? No, thank you!

----

###[[Boost].SML](http://boost-ext.github.io/sml) - More realistic example (System)

```cpp
struct Connection {
  auto operator()() const {
    return transition_table{
      "Disconnected"_s(H) + connect / []{establish();}               = "Connecting"_s,
      "Connecting"_s      + established                              = "Connected"_s,
      "Connected"_s       + ping [ is_valid ] / []{resetTimeout();}
      "Connected"_s       + timeout / []{establish();}               = "Connecting"_s,
      "Connected"_s       + disconnect / []{close();}                = "Disconnected"_s
    };
  };
};
```
<!-- .element: style="margin-left:-10%; width:120%" -->

```cpp
struct System {
  auto operator()() const {
    return transition_table{
     * "Idle"_s          + power_up [ has_battery and is_healthy] / connect = state<Connection>,
       state<Connection> + suspend                                          = "Suspended"_s,
       "Suspended"_s     + resume                                           = state<Connection>,
     // ------------------------------------------------------------------------------------ //
     * "Watchdog"_s      + tick / resetTimeout
       "Watchdog"_s      + timeout                                          = X
    };
  }
};
```
<!-- .element: style="margin-left:-10%; width:120%" -->

----

###[[Boost].SML](http://boost-ext.github.io/sml) - More realistic example (System)

```cpp
int main() {
  using namespace sml;
  sm<System> system;

  system.process_event(power_up{});
  assert(system.is(state<Connection>, "Watchdog"_s));

  system.process_event(suspend{});
  assert(system.is("Suspended"_s, "Watchdog"_s));

  system.process_event(timeout{});
  assert(system.is(X)); // true if any state is in terminated state (X)
}
```

----

##[Boost].SML vs Boost.MSM-eUML vs Boost.Statechart

----

##Overview

| Library     | [Boost].SML | Boost.MSM-eUML  | Boost.Statechart |
| --------    | ----------- | --------------- | ---------------- |
| Standard    | C++14       | C++98/03        | C++98/03         |
| Version     | 1.0.1       | 1.63            | 1.63             |
| License     | Boost 1.0   | Boost 1.0       | Boost 1.0        |
| Linkage     | header only | header only     | header only      |

----

##[Benchmark](https://github.com/boost-ext/sml/tree/master/benchmark/complex)  (Same as before)

![Benchmark](images/benchmark.png)

| Events | States | Transitions | Process Events |
| ------ | ------ | ----------- | -------------- |
| 50 | 50 | 50 | 1'000'000 |

----

##[Benchmark](https://github.com/boost-ext/sml/tree/master/benchmark/complex) - Results

|                  | [Boost].SML    | Boost.MSM-eUML   | Boost.Statechart   |
|------------------|----------------|------------------|--------------------|
| Compilation time |         0.582s |        1m15.935s |             5.671s |
| Execution time   |          622ms |        664ms     |             2282ms |
| Memory usage     |             1b |        120b      |             224b   |
| Executable size  |            34K |        611K      |             211K   |

==============================================================================

##[[Boost].SML](http://boost-ext.github.io/sml) Design in a nutshell

![design](images/design.png)

----

##Transitional Concept

```cpp
template <class T>
concept bool transitional() {
  return requires(T transition) {
    typename T::src_state;
    typename T::dst_state;
    typename T::event;
    T::property; // initial, history state, etc.

    { transition.execute(const typename T::Event&) } -> optional<state_t>;
  }
};
```
<!-- .element: style="width:100%" -->

----

##Front end

###Domain Specific Language (DSL)

```cpp
static_assert(std::is_same<
  decltype(
    transition_table{
      * "Disconnected"_s + connect / establish = "Connecting"_s,
        "Connecting"_s   + established         = "Connected"_s
    }
  ),

  transition_table<
    // ---------------------------------------------------------------------------------- //
    //         src_state              dst_state            event        guard   action    //
    // ---------------------------------------------------------------------------------- //
    transition<state<"Disconnected">, state<"Connecting">, connect,     always, establish>,
    transition<state<"Connecting">,   state<"Connected">,  established, always, none>
  >
>{});
```
<!-- .element: style="margin-left:-8%; width:115%" -->

----

##Back-end

###Generated at Compile Time Mapping per Event and State

```cpp
using mappings_t = type_list<
  pair<connect, type_list<
    transitions<
      transition<state<"Disconnected">, state<"Connecting">, connect, always, establish>
    >,
    transitions<>, // Connecting (unexpected event)
    transitions<>  // Connected  (unexpected event)
  >,
  pair<established, type_list<
    transitions<>, // Disconnected (unexpected event)
    transitions<
      transition<state<"Connecting">, state<"Connected">,  established, always, none>
    >,
    transitions<>  // Connected (unexpected event)
  >
>;
```
<!-- .element: style="margin-left:-8%; width:112%" -->

----

##Back-end

###Process Event - Jump Table - (Simplified)

```cpp
template<class TEvent>
constexpr void process_event(const TEvent& event) {
  process_event_impl(event, get_mappings_t<TEvent>{});
}

template<class... Transitions, class TEvent>
constexpr void process_event_impl(const TEvent& event, type_list<Transitions...>) {
  const static (*dispatch_table[])(const TEvent&) = {
    &Transitions::template execute<TEvent>...
  };
  dispatch_table[current_state](event); // Complexity: O(1) - jump table
}
```
<!--[> .element: style="width:105%" <]-->

==============================================================================

##Summary

----

##If you like it or not, your code won't be stateful (most likely)

----

##Implicit/Hand written state machines are hard to
  * Reason about
  * Maintain / Extend
  * Test

----

##State Machines are more than just simple transitions
  * [UML-2.5](http://www.omg.org/spec/UML/2.5/)

----

##Leveraging Zero-cost libraries will boost your design and/or perfrormance
  * [[Boost].SML](http://boost-ext.github.io/sml/) / [Boost.MSM](http://www.boost.org/doc/libs/1_63_0/libs/msm/doc/HTML/index.html)

==============================================================================

##Questions?

| [Boost].SML |  |
| ------- | ------------- |
| Documentation | http://boost-ext.github.io/sml |
| Source Code | https://github.com/boost-ext/sml |
| Try it online! | http://boost-ext.github.io/sml/examples |
|                | https://godbolt.org/g/UX6eWt |
<!-- .element: style="margin-left:-13%; width:125%" -->

-

[kris@jusiak.net](mailto:kris@jusiak.net) | [@krisjusiak](https://twitter.com/krisjusiak) | [linkedin.com/in/kris-jusiak](https://www.linkedin.com/in/kris-jusiak)


					</script>
				</section>

			</div>
		</div>

		<script src="lib/js/head.min.js"></script>
		<script src="js/reveal.js"></script>

		<script>

			// Full list of configuration options available at:
			// https://github.com/hakimel/reveal.js#configuration
			Reveal.initialize({

        // Display controls in the bottom right corner
        controls: true,

        // Display a presentation progress bar
        progress: true,

        // Display the page number of the current slide
        slideNumber: true,

        // Push each slide change to the browser history
        history: true,

        // Enable keyboard shortcuts for navigation
        keyboard: true,

        // Enable the slide overview mode
        overview: true,

        // Vertical centering of slides
        center: true,

        // Enables touch navigation on devices with touch input
        touch: true,

        // Loop the presentation
        loop: false,

        // Change the presentation direction to be RTL
        rtl: false,

        // Turns fragments on and off globally
        fragments: false,

        // Flags if the presentation is running in an embedded mode,
        // i.e. contained within a limited portion of the screen
        embedded: false,

        // Flags if we should show a help overlay when the questionmark
        // key is pressed
        help: true,

        // Flags if speaker notes should be visible to all viewers
        showNotes: false,

        // Number of milliseconds between automatically proceeding to the
        // next slide, disabled when set to 0, this value can be overwritten
        // by using a data-autoslide attribute on your slides
        autoSlide: 0,

        // Stop auto-sliding after user input
        autoSlideStoppable: true,

        // Enable slide navigation via mouse wheel
        mouseWheel: true,

        // Hides the address bar on mobile devices
        hideAddressBar: true,

        // Opens links in an iframe preview overlay
        previewLinks: false,

        // Transition style
        transition: 'convex', // none/fade/slide/convex/concave/zoom

        // Transition speed
        transitionSpeed: 'default', // default/fast/slow

        // Transition style for full page slide backgrounds
        backgroundTransition: 'default', // none/fade/slide/convex/concave/zoom

        // Number of slides away from the current that are visible
        viewDistance: 3,

        // Parallax background image
        parallaxBackgroundImage: '', // e.g. "'https://s3.amazonaws.com/hakim-static/reveal-js/reveal-parallax-1.jpg'"

        // Parallax background size
        parallaxBackgroundSize: '', // CSS syntax, e.g. "2100px 900px"

        // Number of pixels to move the parallax background per slide
        // - Calculated automatically unless specified
        // - Set to 0 to disable movement along an axis
        parallaxBackgroundHorizontal: null,
        parallaxBackgroundVertical: null,

				// Optional reveal.js plugins
				dependencies: [
					{ src: 'lib/js/classList.js', condition: function() { return !document.body.classList; } },
					{ src: 'plugin/markdown/marked.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
					{ src: 'plugin/markdown/markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
					{ src: 'plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } },
					{ src: 'plugin/zoom-js/zoom.js', async: true },
					{ src: 'plugin/notes/notes.js', async: true }
				]
			});

		</script>

	</body>
</html>
